package nl.hu.lcv.agents;
import jade.core.Agent;
import jade.core.behaviours.Behaviour;
import jade.core.behaviours.CyclicBehaviour;
import jade.core.behaviours.TickerBehaviour;
import jade.domain.DFService;
import jade.domain.FIPAException;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAAgentManagement.ServiceDescription;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;
import jade.lang.acl.UnreadableException;
import jade.wrapper.AgentContainer;
import jade.wrapper.AgentController;

import java.util.ArrayList;
import java.util.Arrays;

import nl.hu.lcv.blackboard.client.BlackboardClientUtils;
import nl.hu.lcv.blackboard.data.PostItProtos.PostIt;
import nl.hu.lcv.blackboard.data.PostItProtos.PostItBox;
import nl.hu.lcv.translation.StepTranslationUtils;


public class EquipletAgent extends Agent {
	private long currentTimeStep; 
	private CurrentAvailabilityDatabase currDb = null;
	private ArrayList<String> currentNodes = new ArrayList<String>();
	private String myEquipletNodeName;
	
	private class TranslateStepBlackboardBehaviour extends Behaviour
    {    	
    	private PostItBox ReadBox;
    	private int stepcount = 0;
    	private ArrayList<PostIt> highProductStepsToProcess = null;
		private ArrayList<PostIt> lowProductSteps = new ArrayList<PostIt>();
    			
		@Override
		public void action() {	
			
			switch(stepcount)
			{				
				case(0):
					//Read from blackboard
					ReadBox = PostItBox.newBuilder()
					.setIsWrite(false)
					.setReadOwner(getLocalName())
					.setZone("BB2")
					.setCurrentTimeStep(currentTimeStep)
					.setFilter(PostItBox.Filter.newBuilder().setFiltername("PostItFilter"))
					.build();			 	
			 		//Look for new postits on the blackboard
				    ReadBox = BlackboardClientUtils.readFromBlackboard(ReadBox);			
				    if(ReadBox.getPostItsList().size() > 0)
					{
				    	highProductStepsToProcess = new ArrayList<PostIt>(ReadBox.getPostItsList());
						stepcount++;
					}
					else
					{
						System.out.println(getLocalName() + ": Read unsuccesfull, no entry");
						stepcount = 3;
					}
				    break;
				case(1):
					//Translate					
					
					for(PostIt postIt : highProductStepsToProcess)
					{
						//System.out.println(getLocalName() + ": Read succesfull: " + postIt.getPayload() + " TimeStep = :" + postIt.getTimestep() + " Productnr: " + postIt.getProductRef()) ;
						//Get the moduleSteps
						ArrayList<String> moduleSteps = StepTranslationUtils.TranslateProductStep(postIt.getPayload());
					    for(int i = 0; i<moduleSteps.size();i++)
					    {
					    	String m = moduleSteps.get(i);
					    	System.out.println(getLocalName() + ": Split step: " + m);
					    	//Create PostIt for the moduleStep and save it
					    	PostIt p = PostIt.newBuilder()
					    			.setOrdinal(i)
					    			.setOwner(myEquipletNodeName)
					    			.setPayload(m.toString())
					    			.build();									 
					    	lowProductSteps.add(p);					    	
					    }					
					}				
					stepcount++;
					break;
				case(2):
					//Put the modulesteps on BB3
					PostItBox boxToWrite = PostItBox.newBuilder()
					.setIsWrite(true)
					.setZone("BB3")
					.addAllPostIts(lowProductSteps)
					.build();		    		
		    		BlackboardClientUtils.writeToBlackboard(boxToWrite);
		    		stepcount++;
		    		break;
					
			}
		}

		@Override
		public boolean done() {
			return stepcount == 3;
		}
    }
	
	
	
	private class ReceiveCurrentTimeStepBehaviour extends CyclicBehaviour
	{
		@Override
		public void action() 
		{
			MessageTemplate mt = MessageTemplate.and(MessageTemplate.MatchPerformative(ACLMessage.INFORM), MessageTemplate.MatchOntology("Boef-ontology"));
			
			try {
				ACLMessage msg = myAgent.receive(mt);
				if(msg != null)
				{					
						currentTimeStep = (Long)msg.getContentObject();
						System.out.println(currentTimeStep);
						addBehaviour(new TranslateStepBlackboardBehaviour());				
				}
			}
			catch (UnreadableException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
			}						
			block();
		}
	}
	
	/**
	 * The Class InitiateEquipletAgentBehaviour.
	 * This behaviour checks if a new equiplet has entered the system and creates an agent for it.
	 */
	private class CheckNewNodesBehaviour extends TickerBehaviour
	{
			public CheckNewNodesBehaviour(Agent a, long period) {
				super(a, period);			
			}
								
			protected void onTick() 
			{				
				PostItBox ReadBox;
				ReadBox = PostItBox.newBuilder()
						.setIsWrite(false)
						.setReadOwner(getLocalName())
						.setCurrentTimeStep(currentTimeStep)
						.setZone("BB3")
						.setFilter(PostItBox.Filter.newBuilder().setFiltername("PostItFilter"))
						.build();			 	
				 		//Look for new postits on the blackboard
				ReadBox = BlackboardClientUtils.readFromBlackboard(ReadBox);
				
				for(PostIt p : ReadBox.getPostItsList())
				{
					System.out.println(p.getPayload());
					if(!currentNodes.contains(p.getPayload()))
						currentNodes.add(p.getPayload());
										
					if(currentNodes.containsAll(Arrays.asList(new String[]{"GRIPPER","DELTA","VISION"})))
					{
						currDb.addStepForEquiplet(getLocalName(), "PLACE");
						System.out.println("yet!");
					}
					else
						System.out.println("Not yet");
				}	
			}		
	 }
	
	
 	 public void setup()
     {  		
 		this.currDb = (CurrentAvailabilityDatabase) getArguments()[0]; 
 		myEquipletNodeName = getLocalName().substring(0, getLocalName().indexOf("Agent"));
 		DFAgentDescription dfd = new DFAgentDescription(); 
 	    dfd.setName(getAID()); 
 	    ServiceDescription sd = new ServiceDescription(); 
 	    sd.setType("Equiplet"); 
 	    sd.setName("Equiplet-Agent");
 	    ServiceDescription sr = new ServiceDescription(); 
	    sr.setType("TimeReceiver"); 
	    sr.setName("TimeSync");
 	    
	    dfd.addServices(sr);
 	    dfd.addServices(sd); 
 	    try { 
 	      DFService.register(this, dfd); 
 	    } 
 	    catch (FIPAException fe) { 
 	      fe.printStackTrace();
 	    }
 		addBehaviour(new ReceiveCurrentTimeStepBehaviour());
 		addBehaviour(new CheckNewNodesBehaviour(this, 4000)); 		
     }
 	 
 	 public void takeDown()
 	 {
 		 try 
 		 { 
 		      DFService.deregister(this); 
 		 } 
 		 catch (FIPAException fe) 
 		 { 
 		      fe.printStackTrace(); 
 		 } 	 
 	 }	 
}
